001    package EVolve.util.settings;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    public class IniFile {
007        private Map sections;
008        private File file;
009        private boolean modified;
010    
011        public IniFile(String fileName) throws IOException {
012            this(new File(fileName));
013        }
014    
015        public IniFile(File file) throws IOException {
016            this.sections = new LinkedHashMap();
017            this.file = file;
018            
019            if (file.exists()) {
020                load();
021            }
022        }
023    
024        private static String parseValue(String value) {
025            if (value == null) {
026                return null;
027            }
028    
029            value = value.trim();
030            if (value.length() > 0) {
031                char s[] = value.toCharArray();
032                if (s[0] == '"') {
033                    // Find the next '"'
034                    boolean escaped = false;
035                    int i;
036    loop:
037                    for (i = 1; i < s.length; i++) {
038                        if (escaped) {
039                            escaped = false;
040                        } else {
041                            switch (s[i]) {
042                                case '\\':
043                                    escaped = true;
044                                    break;
045                                case '"':
046                                    break loop;
047                                default:
048                                    break;
049                            }
050                        }
051                    }
052    
053                    if (s[i] == '"') {
054                        if (i < s.length - 1) {
055                            String commentStr = value.substring(i+1).trim();
056                            if (!commentStr.startsWith(";")) {
057                                throw new RuntimeException("Expression expected to be a comment");
058                            }
059                        } 
060                        value = value.substring(1, i);
061                    }
062                }
063            }
064    
065            return unescape(value);
066        }
067    
068        public static String escape(String value) {
069            if (value == null) {
070                return null;
071            }
072    
073            String result = "";
074            char[] s = value.toCharArray();
075            for (int i = 0; i < s.length; i++) {
076                char c = s[i];
077                switch (c) {
078                    case '?':
079                        result += "\\?";
080                        break;
081                    case '\b':
082                        result += "\\b";
083                        break;
084                    case '\t':
085                        result += "\\t";
086                        break;
087                    case '\n':
088                        result += "\\n";
089                        break;
090                    case '\f':
091                        result += "\\f";
092                        break;
093                    case '\r':
094                        result += "\\r";
095                        break;
096                    case '"':
097                        result += "\\\"";
098                        break;
099                    case '\'':
100                        result += "\\'";
101                        break;
102                    case '\\':
103                        result += "\\\\";
104                        break;
105                    default:
106                        result += c;
107                        break;
108                }
109            }
110    
111            return result;
112        }
113    
114        public static String unescape(String value) {
115            if (value == null) {
116                return null;
117            }
118    
119            String result = "";
120            char[] s = value.toCharArray();
121            for (int i = 0; i < s.length; i++) {
122                char c = s[i];
123                if (c == '\\') {
124                    if (i == (s.length - 1)) {
125                        return result + '\\';
126                    } else {
127                        c = s[++i];
128                        switch (c) {
129                            case '?':
130                                result += '?';
131                                break;
132                            case 'b':
133                                result += '\b';
134                                break;
135                            case 't':
136                                result += '\t';
137                                break;
138                            case 'n':
139                                result += '\n';
140                                break;
141                            case 'f':
142                                result += '\f';
143                                break;
144                            case 'r':
145                                result += '\r';
146                            case 'x':
147                                {
148                                    i++;
149                                    String hexString = "";
150                                    while (i < s.length) {
151                                        c = s[i];
152                                        if (Character.digit(c, 16) >= 0) {
153                                            hexString += c;
154                                            i++;
155                                        } else {
156                                            break;
157                                        }
158                                    }
159    
160                                    if (hexString.length() <= 0) {
161                                        i--;
162                                        result += "\\x";
163                                    } else {
164                                        result += (char) Short.parseShort(hexString, 16);
165                                    }
166                                }
167                                break;
168                            case '0':
169                            case '1':
170                            case '2':
171                            case '3':
172                            case '4':
173                            case '5':
174                            case '6':
175                            case '7':
176                            case '8':
177                            case '9':
178                                {
179                                    String octalString = "";
180                                    while (i < s.length) {
181                                        c = s[i];
182                                        if (Character.digit(c, 8) >= 0) {
183                                            octalString += c;
184                                            i++;
185                                        } else {
186                                            break;
187                                        }
188                                    }
189    
190                                    if (octalString.length() <= 0) {
191                                        result += "\\";
192                                    } else {
193                                        result += (char) Short.parseShort(octalString, 8);
194                                    }
195                                }
196                                break;
197                            default:
198                                result += '\\' + c;
199                                break;
200                        }
201                    }
202                } else {
203                    result += c;
204                }
205            }
206    
207            return result;
208        }
209    
210        public void load() throws IOException {
211            BufferedReader in = new BufferedReader(new FileReader(file));
212            String line;
213            String currentSection = null;
214    
215            while ((line = in.readLine()) != null) {
216                line = line.trim();
217                if (line.length() <= 0) {
218                    continue;
219                }
220    
221                char c = line.charAt(0);
222    
223                switch (c) {
224                    case ';':
225                        continue;
226                    case '[':
227                        currentSection = line.substring(1, line.length() - 1);
228                        put(currentSection);
229                        break;
230                    default:
231                        {
232                            int eqPos = line.indexOf('=');
233                            if (eqPos < 0) {
234                                throw new RuntimeException("Invalid INI file format -- Expected key=value");
235                            }
236    
237                            String key = line.substring(0, eqPos).trim();
238                            String value = parseValue(line.substring(eqPos + 1));
239    
240                            put(currentSection, key, value);
241                        }
242                        break;
243                }
244            }
245    
246            modified = false;
247    
248            in.close();
249        }
250    
251        public void write() throws IOException {
252    
253            PrintStream out = new PrintStream(new FileOutputStream(file));
254            
255            Iterator sectionIt = sections.keySet().iterator();
256            while (sectionIt.hasNext()) {
257                Object sectionName = (String) sectionIt.next();
258                Map keysToValues = (Map) sections.get(sectionName);
259    
260                out.println("[" + sectionName + "]");
261                Iterator keysIt = keysToValues.keySet().iterator();
262                while (keysIt.hasNext()) {
263                    String key = (String) keysIt.next();
264                    String value = (String) keysToValues.get(key);
265                    out.println(key + "=\"" + escape(value) + "\"");
266                }
267                out.println();
268            }
269        }
270    
271        public boolean contains(String section) {
272            if (section == null) {
273                return false;
274            }
275    
276            return sections.containsKey(section);
277        }
278    
279        public boolean contains(String section, String key) {
280            if (section == null || key == null) {
281                return false;
282            }
283    
284            if (sections.containsKey(section)) {
285                Map keysToValues = (Map) sections.get(section);
286                return keysToValues.containsKey(key);
287            }
288    
289            return false;
290        }
291    
292        public boolean put(String section) {
293            if (section == null) {
294                return false;
295            }
296    
297            if (!sections.containsKey(section)) {
298                sections.put(section, new LinkedHashMap());
299            }
300    
301            modified = true;
302            return true;
303        }
304    
305        public boolean put(String section, String key, String value) {
306            if (section == null || key == null) {
307                return false;
308            }
309    
310            if (value == null) {
311                value = "";
312            }
313    
314            Map keysToValues;
315            if (sections.containsKey(section)) {
316                keysToValues = (Map) sections.get(section);
317            } else {
318                keysToValues = new LinkedHashMap();
319                sections.put(section, keysToValues);
320            }
321    
322            keysToValues.put(key, value);
323    
324            modified = true;
325            
326            return true;
327        }
328    
329        public String get(String section, String key) {
330            if (sections.containsKey(section)) {
331                Map keysToValues = (Map) sections.get(section);
332                if (keysToValues.containsKey(key)) {
333                    return (String) keysToValues.get(key);
334                }
335            }
336    
337            return null;
338        }
339    
340        public Set getSections() {
341            return new HashSet(sections.keySet());
342        }
343    
344        public Set getKeys(String section) {
345            if (section == null) {
346                return null;
347            }
348    
349            if (sections.containsKey(section)) {
350                Map keysToValues = (Map) sections.get(section);
351                return new HashSet(keysToValues.keySet());
352            }
353    
354            return null;
355        }
356    
357        public Set getValues(String section) {
358            if (section == null) {
359                return null;
360            }
361    
362            if (sections.containsKey(section)) {
363                Map keysToValues = (Map) sections.get(section);
364                return new HashSet(keysToValues.values());
365            }
366    
367            return null;
368        }
369    
370        public void close() throws IOException {
371            if (sections != null) {
372                if (modified) {
373                    this.write();
374                }
375                sections = null;
376            }
377        }
378    
379        public void finalize() {
380            try {
381                this.close();
382            } catch (IOException e) {
383                // not much we can do now...
384            }
385        }
386    }
387